﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TvServer.Epg
{
    public class DrService
    {
        internal class Request
        {
            private int _requestId;
            private string _method;
            private string _parameters;

            public int RequestId
            {
                get { return _requestId; }
                set { _requestId = value; }
            }

            public string Method
            {
                get { return _method; }
                set { _method = value; }
            }
            
            public string Parameters
            {
                get { return _parameters; }
                set { _parameters = value; }
            }

            public Request(int requestId, string method, string parameters)
            {
                _requestId = requestId;
                _method = method;
                _parameters = parameters;
            }
        }

        internal class Response
        {
            private string _response;
            private int _requestId;
            private string _result;
            private string _error;
            private ResultList _resultList;

            public int RequestId
            {
                get { return _requestId; }
                set { _requestId = value; }
            }

            public string Result
            {
                get { return _result; }
                set { _result = value; }
            }

            public string Error
            {
                get { return _error; }
                set { _error = value; }
            }

            public ResultList ResultList
            {
                get { return _resultList; }
                set { _resultList = value; }
            }

            public Response(string response)
            {
                _response = response.Trim('{', '}');

                string requestId = GetValue(_response, "id");

                int.TryParse(requestId, out this._requestId);
                _result = GetValue(_response, "result");

                _resultList = new ResultList(_result);
            }

            public static string GetValue(string source, string key)
            {
                string value = null;

                int indexOfKey = source.IndexOf("\"" + key + "\"");
                int indexOfValueStart = source.IndexOf(':', indexOfKey) + 1;
                int indexOfValueEnd = -1;
                if (source.Substring(indexOfValueStart, 1) == "[")
                {
                    ++indexOfValueStart;
                    indexOfValueEnd = source.IndexOf(']', indexOfValueStart);
                }
                else
                {
                    indexOfValueEnd = source.IndexOf(',', indexOfValueStart);
                }
                
                if(indexOfValueEnd > indexOfValueStart)
                    value = source.Substring(indexOfValueStart, indexOfValueEnd - indexOfValueStart);

                return value;
            }
        }

        internal class ResultList
        {
            internal class ResultGroup
            {
                string _result;
                List<ResultEntry> _entryList;

                public string Result
                {
                    get { return _result; }
                    set { _result = value; }
                }

                public List<ResultEntry> EntryList
                {
                    get { return _entryList; }
                    set { _entryList = value; }
                }

                public ResultGroup(string result)
                {
                    _result = result;
                    _entryList = new List<ResultEntry>();

                    int currentPosition = 0;
                    while (currentPosition < result.Length)
                    {
                        int groupStartPosition = result.IndexOf("\"", currentPosition);
                        int groupEndPosition = result.IndexOf(",", groupStartPosition);
                        groupEndPosition = (groupEndPosition == -1) ? result.Length : groupEndPosition;

                        _entryList.Add(new ResultEntry(result.Substring(groupStartPosition, groupEndPosition - groupStartPosition)));

                        currentPosition = groupEndPosition + 1;
                    }
                }

                public string GetEntryValue()
                {
                    return GetEntryValue("", "");
                }
                
                public string GetEntryValue(string key, string defaultValue)
                {
                    ResultEntry resultEntry =_entryList.Find(e => e.Result.Key == key);
                    return (resultEntry == null) ? defaultValue : resultEntry.Result.Value;
                    
                }
            }

            internal class ResultEntry
            {
                KeyValuePair<string, string> _result;

                public KeyValuePair<string, string> Result
                {
                    get { return _result; }
                    set { _result = value; }
                }

                public ResultEntry(string result)
                {
                    _result = new KeyValuePair<string,string>("", result.Trim('"'));

                    if (_result.Value.IndexOf("\":") > -1)
                    {   //KeyValuePair
                        result = result.Replace("\":", "|").Trim('"');

                        string[] keyValueResult = result.Split('|');
                        if (keyValueResult.Length == 2)
                            _result = new KeyValuePair<string, string>(keyValueResult[0].Trim('"'), keyValueResult[1].Trim('"'));
                    }
                }
            }

            private string _result;
            private List<ResultGroup> _groupList;

            public string Result
            {
                get { return _result; }
                set { _result = value; }
            }

            public List<ResultGroup> GroupList
            {
                get { return _groupList; }
                set { _groupList = value; }
            }

            public ResultList(string result)
            {
                _result = result;
                _groupList = new List<ResultGroup>();

                int currentPosition = 0;
                while (currentPosition < result.Length)
                {
                    int groupStartPosition = result.IndexOf('{', currentPosition) + 1;
                    int groupEndPosition = result.IndexOf('}', groupStartPosition);
                    if (groupStartPosition == 0)
                    {
                        groupEndPosition = result.Length;
                        _groupList.Add(new ResultGroup(result));
                    }
                    else
                        _groupList.Add(new ResultGroup(result.Substring(groupStartPosition, groupEndPosition - groupStartPosition)));

                    currentPosition = groupEndPosition + 1;
                }
            }
        }

        private int _nextRequestId = 0;

        public List<Channel> GetChannels(ChannelType channelType)
        {
            List<Channel> channelList = new List<Channel>();
            Request request = new Request(++_nextRequestId, "getChannels", "{ \"type\" : \"" + channelType.ToString().ToLower() + "\" }");
            Response response = CallService(request);
            if (response != null && response.Error == null)
            {
                foreach (ResultList.ResultGroup resultGroup in response.ResultList.GroupList)
                {
                    Channel channel = new Channel();
                    channel.ChannelGroup = int.Parse(resultGroup.GetEntryValue("channel_group", "0"));
                    channel.CountryCode = int.Parse(resultGroup.GetEntryValue("country_code", "0"));
                    channel.DrChannel = bool.Parse(resultGroup.GetEntryValue("dr_channel", "false"));
                    channel.Name = resultGroup.GetEntryValue("name", "");
                    channel.SourceUrl = resultGroup.GetEntryValue("source_url", "");
                    string type = resultGroup.GetEntryValue("type", "");
                    if (type != "")
                    {
                        type = type.Substring(0, 1).ToUpper() + type.Substring(1, type.Length - 1).ToLower();
                        channel.Type = (ChannelType)Enum.Parse(typeof(ChannelType), type);
                    }
                    channel.WwwUrl = resultGroup.GetEntryValue("www_url", "");

                    channelList.Add(channel);
                }
            }
            
            return channelList;
        }

        public List<Schedule> GetSchedules(Channel channel, DateTime date)
        {
            List<Schedule> scheduleList = new List<Schedule>();
            Request request = new Request(++_nextRequestId, "getSchedule", "{ \"channel_source_url\" : \"" + channel.SourceUrl + "\", \"broadcastDate\" : \"" + date.ToString("yyyy-MM-dd") + "\" }");
            Response response = CallService(request);
            if (response != null && response.Error == null)
            {
                foreach (ResultList.ResultGroup resultGroup in response.ResultList.GroupList)
                {
                    Schedule schedule = new Schedule(channel);
                    schedule.PgSeriesName = resultGroup.GetEntryValue("pg_series_name", "");
                    schedule.PgStart = DateTime.Parse(resultGroup.GetEntryValue("pg_start", DateTime.MinValue.ToString()));
                    schedule.PgStop = DateTime.Parse(resultGroup.GetEntryValue("pg_stop", DateTime.MinValue.ToString()));
                    schedule.PpuAudio = resultGroup.GetEntryValue("ppu_audio", "");
                    schedule.PpuDescription = resultGroup.GetEntryValue("ppu_description", "");
                    schedule.PpuIslive =  bool.Parse(resultGroup.GetEntryValue("ppu_islive", "false"));
                    schedule.PpuIsrerun = bool.Parse(resultGroup.GetEntryValue("ppu_isrerun", "false"));
                    schedule.PpuPunchline = resultGroup.GetEntryValue("ppu_punchline", "");
                    schedule.PpuSourceUrl = resultGroup.GetEntryValue("ppu_source_url", "");
                    schedule.PpuStreamingLive = bool.Parse(resultGroup.GetEntryValue("ppu_streaming_live", "false"));
                    schedule.PpuStreamingOd = bool.Parse(resultGroup.GetEntryValue("ppu_streaming_od", "false"));
                    schedule.PpuTimestampBroadcastdate = DateTime.Parse(resultGroup.GetEntryValue("ppu_timestamp_broadcastdate", DateTime.MinValue.ToString()));
                    schedule.PpuTitle = resultGroup.GetEntryValue("ppu_title", "");
                    schedule.PpuTitleAlt = resultGroup.GetEntryValue("ppu_title_alt", "");
                    schedule.PpuVideo = resultGroup.GetEntryValue("ppu_video", "");
                    schedule.PrdEpisodeNumber = int.Parse(resultGroup.GetEntryValue("prd_episode_number", "-1"));
                    schedule.PrdGenreCode = resultGroup.GetEntryValue("prd_genre_code", "");
                    schedule.PrdGenreText = resultGroup.GetEntryValue("prd_genre_text", "");
                    schedule.PrdIsOwnProduction = bool.Parse(resultGroup.GetEntryValue("prd_is_own_production", "false"));
                    schedule.PrdProdcountry = resultGroup.GetEntryValue("prd_prodcountry", "");
                    schedule.PrdProdyear = DateTime.Parse(resultGroup.GetEntryValue("prd_prodyear", DateTime.MinValue.ToString()));
                    schedule.PrdSeriesTitle = resultGroup.GetEntryValue("prd_series-title", "");
                    schedule.ProCategory = resultGroup.GetEntryValue("pro_category", "");
                    schedule.ProIntention = resultGroup.GetEntryValue("pro_intention", "");
                    schedule.ProTargetgroup = resultGroup.GetEntryValue("pro_targetgroup", "");
                    schedule.ProTitle = resultGroup.GetEntryValue("pro_title", "");

                    scheduleList.Add(schedule);
                }
            }
            return scheduleList;
        }

        public List<DateTime> GetAvailableBroadcastDates()
        {
            List<DateTime> availableBroadcastDateList = new List<DateTime>();
            Request request = new Request(++_nextRequestId, "availableBroadcastDates", "{ /* void */ }");
            Response response = CallService(request);
            if (response != null && response.Error == null)
            {
                foreach (ResultList.ResultGroup resultGroup in response.ResultList.GroupList)
                {
                    foreach (ResultList.ResultEntry resultEntry in resultGroup.EntryList)
                    {
                        DateTime date = DateTime.Parse(resultEntry.Result.Value);
                        availableBroadcastDateList.Add(date);
                    }
                }
            }

            return availableBroadcastDateList;
        }

        private Response CallService(Request request)
        {
            Response response = null;

            try
            {
                // ToDo: Move URL to some kind of config file
                System.Net.WebRequest webRequest = System.Net.WebRequest.Create("http://beta.dr.dk/Programoversigt2009/DBService.ashx");
                webRequest.Proxy = new System.Net.WebProxy();
                webRequest.Method = "POST";
                webRequest.ContentType = "text/plain; charset=utf-8";
                webRequest.Headers.Add("X-JSON-RPC", request.Method);

                string requestValue = "{\"id\":" + request.RequestId + ",\"method\":\"" + request.Method + "\",\"params\":" + request.Parameters + "}";
                System.IO.Stream requestStream = webRequest.GetRequestStream();

                System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(requestStream, Encoding.UTF8);
                streamWriter.Write(requestValue);
                streamWriter.Close();

                System.Net.WebResponse webResponse = webRequest.GetResponse();
                System.IO.Stream responseStream = webResponse.GetResponseStream();
                System.IO.StreamReader streamReader = new System.IO.StreamReader(responseStream);
                response = new Response(streamReader.ReadToEnd());
                streamReader.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                // Todo: Implement exception handling 
            }

            return response;
        }
    }
}